package bbs.tran;

 
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.ibatis.session.*;
import org.apache.logging.log4j.*; 

import bbs.*;
import bbs.db.*; 
import bbs.model.*; 

public class BbsSystem extends BbsObject {
	
	/**
	 * Log4j logger
	 */
	static final Logger logger = LogManager.getLogger(BbsSystem.class);
	
	private static final BbsSystem bbsSystem = new BbsSystem(); 
	
	/**
	 * 로그인 사용자
	 */
	private Hashtable<HttpSession, User> loginUsers = new Hashtable<HttpSession, User>();
	
	private User nullSessionLoginUer = null;
	
	/**
	 * 사용자 로그인 필요여부.
	 */
	private boolean forcedUserLoginAtFirst = false;

	/**
	 * 생성자
	 * 싱글 인스턴스 패턴
	 */
	private BbsSystem() { 
	}
	
	public static BbsSystem getBbsSystem() {
		return bbsSystem;
	}   

	public boolean isForcedUserLoginAtFirst() {
		return forcedUserLoginAtFirst;
	}

	public void setForcedUserLoginAtFirst(boolean forcedUserLoginAtFirst) {
		this.forcedUserLoginAtFirst = forcedUserLoginAtFirst;
	}

	/**
	 * SQL 세션 체크 
	 * @return
	 */
	public SqlSession getSqlSession() {
		
		SqlSession sqlSession =  ConnectionFactory.getConnectionFactory().getSqlSession(); 
		
		return sqlSession; 
	} 
	
	/**
	 * 로그인 사용자 반환
	 * @return
	 */
	public User getLoginUser( HttpServletRequest request ) { 
		if( request == null ) {
			return this.nullSessionLoginUer;
		} else {  
			HttpSession hSession = request.getSession();
			
			User loginUser = this.loginUsers.get( hSession );
			return loginUser;
		}
	}
	
	/**
	 * 로그인 사용자 설정ㄷ 
	 * @param request
	 * @param user
	 */
	public void setLoginUser( HttpServletRequest request , User user ) {
		if( request == null ) {
			this.nullSessionLoginUer = user;
		} else { 
		
			HttpSession hSession = request.getSession();
			
			if( user == null ) {
				this.loginUsers.remove( hSession );
				
				hSession.setAttribute( "loginUserName", null );
			} else {  
				this.loginUsers.put( hSession, user );
				
				hSession.setAttribute( "loginUserName", user.getUserName() );
			}
		}
	}
	
	/**
	 * 시스템을 종료한다.
	 * @return
	 */
	public String doSystemExit( HttpServletRequest request ) {
		
		String error = null;  
		
		User loginUser = this.getLoginUser(request);
		
		if( loginUser != null ) {
			loginUser.logout();
		}
		
		this.setLoginUser(request, null);
		
		return error;
	}
	
	/**
	 * 사용자 목록
	 * @param params
	 * @return
	 */
	public UserList getAllUsers( SqlSession session, Params params) { 
		
		List<User> list = session.selectList( "bbs.selectFromBbsUser" , params ); 
		
		UserList userList = new UserList( list );
		
		return userList ;
	} 
	
	/**
	 * 사용자 정보를 반환한다.
	 * @param session
	 * @param userName
	 * @return
	 */
	public User getUser( SqlSession session, String userName ) {
		
		Params params = new Params();
		params.put( "USER_NAME" , userName );
		
		User user = session.selectOne( "bbs.selectFromBbsUser", params );
		
		return user;
	}
	
	/**
	 * 사용자 이름, 암호에 해당하는 사용자를 DB에 생성한다.
	 * 
	 * @param userName
	 * @param passwd
	 * @return
	 */
	public int createNewUser( HttpServletRequest request, SqlSession session, String userName, String passwd, Boolean isAdmin ) {
		
		logger.trace( "doLogin: userName: " + userName + "; passwd: " + passwd + "; isAdmin: " + isAdmin );
		
		isAdmin = isAdmin == null ? false : isAdmin; 
		
		Params params = new Params();
		params.put( "USER_NAME", userName );
		params.put( "USER_PASSWD", passwd ); 
		params.put( "USER_ROLE" ,  isAdmin ? 1 : 0 );
		
		int updCnt = session.insert( "bbs.insertIntoBbsUser" , params );  
		
		if( updCnt < 1 ) {
			session.rollback();
			
			logger.trace( "Failed to create a new user." ); 
		} else if( updCnt > 0 ) { 
			session.commit();
			
			logger.trace( "Success to create a new user." );
		}
		
		return updCnt ;
	} 
	
	
	/**
	 * 사용자 이름, 암호에 해당하는 로그인 한 사용자 정보를 가져온다.
	 * 
	 * @param userName
	 * @param passwd
	 * @return
	 */
	public User doLogin( HttpServletRequest request, SqlSession session, String userName, String passwd, Boolean isAdmin ) {
		
		logger.trace( "doLogin: userName: " + userName + "; passwd: " + passwd + "; isAdmin: " + isAdmin );
		
		isAdmin = isAdmin == null ? false : isAdmin; 
		
		Params params = new Params();
		params.put( "USER_NAME", userName );
		params.put( "USER_PASSWD", passwd );  
		
		User user = session.selectOne( "bbs.selectFromBbsUser" , params ); 
		
		if( user != null && user.isLoginSucess() ) {
			// 패스워드와 어드민 로그인 모드가 일치할 경우,
			this.setLoginUser(request, user); 
		} 
		
		logger.trace( "Done login." );
		
		return user ;
	}
	
	/**
	 * 로그 아웃 한다.
	 */
	public void doLogout( HttpServletRequest request ) {
		
		User loginUser = this.getLoginUser(request);
		
		if( loginUser != null ) {
			loginUser.logout();
		}
		
		this.setLoginUser(request, null );
	}

	/**
	 * 게시판 목록
	 * @param boardId 게시판 아이디
	 * @param loginUser 로그인 사용자
	 * @param boardValid 유효한 게시판만 조회 
	 * @return
	 */
	public BoardList getAllBoards( SqlSession session, User loginUser, Boolean boardValid, Integer viewPageNo ) { 
		
		/*
		#{ BOARD_USER_ID ,		jdbcType=NUMERIC } AS param_board_user_id ,
        #{ BOARD_ID , 	 	 	jdbcType=VARCHAR } AS param_board_id ,
        #{ BOARD_VALID ,			jdbcType=NUMERIC } AS param_board_valid ,
        #{ LOG_PAPER_USER_ID , 	jdbcType=VARCHAR } AS param_log_paper_user_id ,
        */
		
		Integer boardId = null;
		Integer logPaperUserId = ( loginUser == null ) ? null : loginUser.getUserId() ; 
		
		Params params = new Params();
		
		params.put( "BOARD_USER_ID", "" );
		params.put( "BOARD_ID", boardId );
		params.put( "BOARD_VALID", boardValid );
		params.put( "LOG_PAPER_USER_ID", logPaperUserId );
		
		params.put( "VIEW_PAGE_NO", viewPageNo );
		
		logger.trace( "getAllBoards: " + params );		
		
		List<Board> list = session.selectList( "bbs.selectFromBoard" , params );  
		
		logger.trace( "Done getAllBoards. size: " + list.size() );
		
		if( list.size() > 0 ) {
			logger.trace( "First Entity: " + list.get( 0 ) );
		}
		
		BoardList boardList = new BoardList( list );
		
		return boardList ;
	} 
	
	/**
	 * 게시판 
	 * @param boardId 게시판 아이디
	 * @param loginUser 로그인 사용자
	 * @param boardValid 유효한 게시판만 조회
	 * @return
	 */
	public Board getBoard(  SqlSession session, Integer boardId , User loginUser, Integer boardValid ) {
		
		if( boardId == null || boardId < 1 ) {
			return null;
		} 
		
		/*
		#{ BOARD_USER_ID ,		jdbcType=NUMERIC } AS param_board_user_id ,
        #{ BOARD_ID , 	 	 	jdbcType=VARCHAR } AS param_board_id ,
        #{ BOARD_VALID ,			jdbcType=NUMERIC } AS param_board_valid ,
        #{ LOG_PAPER_USER_ID , 	jdbcType=VARCHAR } AS param_log_paper_user_id ,
        */
		
		Integer logPaperUserId = ( loginUser == null ) ? null : loginUser.getUserId() ; 
		
		Params params = new Params();
		params.put( "BOARD_USER_ID", "" );
		params.put( "BOARD_ID", boardId );
		params.put( "BOARD_VALID", boardValid );
		params.put( "LOG_PAPER_USER_ID", logPaperUserId );
		
		logger.trace( "getBoard: " + params );
		
		Board entity = session.selectOne( "bbs.selectFromBoard" , params ); 
		
		logger.trace( "Done getBoard. size: " + 1 );
		
		return entity ;
	} 
	
	/**
	 * 문서 목록을 가져온다.
	 * @param session
	 * @param board 조회 게시판
	 * @param parentPaper 조회 상위 문서
	 * @param paperId 조회 문서 아이디
	 * @param paperUser 조회 문서 소유자
	 * @param paperName 조회 문서 이름
	 * @param paperValid 조회 문서 유효성
	 * @return
	 */
	public PaperList getAllPapers( SqlSession session, Board board, Paper parentPaper, Integer paperId, User paperUser , String paperName , Boolean paperValid , Integer viewPageNo ) {
		
		if( board == null ) {
			return null;
		}
		
		/*
		      #{ PAPER_BOARD_ID ,	jdbcType=NUMERIC } AS param_board_id ,
		      #{ PAPER_PARENT_ID ,	jdbcType=NUMERIC } AS param_paper_parent_id ,
		      #{ PAPER_ID ,			jdbcType=NUMERIC } AS param_paper_id ,
		      #{ PAPER_NAME ,		jdbcType=NUMERIC } AS param_paper_name ,
		      #{ PAPER_USER_ID ,	jdbcType=NUMERIC } AS param_paper_user_id ,
		      #{ PAPER_VALID ,		jdbcType=NUMERIC } AS param_paper_valid ,
		 */
		Integer boardId = board == null ? null : board.getBoardId();
		Integer paperParentId = parentPaper == null ? null : parentPaper.getPaperId();
		Integer userId = paperUser == null ? null : paperUser.getUserId();
		
		Params params = new Params();
		params.put( "PAPER_BOARD_ID", boardId );
		params.put( "PAPER_PARENT_ID", paperParentId );
		params.put( "PAPER_ID", paperId );
		params.put( "PAPER_NAME", paperName );
		params.put( "PAPER_USER_ID", userId );
		params.put( "PAPER_VALID", paperValid );
		
		params.put( "VIEW_PAGE_NO", viewPageNo );
		
		logger.trace( "getAllPapers: " + params );
		
		List<Paper> list = session.selectList( "selectFromPaper", params );
		
		logger.trace( "Done getAllPapers: size: " + list.size() );
		
		PaperList paperList = new PaperList( list );
		
		return paperList ;
		
	}
	
	/**
	 * 문서를 가져온다.
	 * @param session
	 * @param board 조회 게시판
	 * @param parentPaper 조회 상위 문서
	 * @param paperId 조회 문서 아이디
	 * @param paperUser 조회 문서 소유자
	 * @param paperName 조회 문서 이름
	 * @param paperValid 조회 문서 유효성
	 * @return
	 */
	public Paper getPaper( SqlSession session, Board board, Paper parentPaper, Integer paperId, User paperUser , String paperName, Integer paperValid ) {
		
		if( board == null || paperId == null ) {
			return null;
		}
		
		/*
		      #{ PAPER_BOARD_ID ,	jdbcType=NUMERIC } AS param_board_id ,
		      #{ PAPER_PARENT_ID ,	jdbcType=NUMERIC } AS param_paper_parent_id ,
		      #{ PAPER_ID ,			jdbcType=NUMERIC } AS param_paper_id ,
		      #{ PAPER_NAME ,		jdbcType=NUMERIC } AS param_paper_name ,
		      #{ PAPER_USER_ID ,	jdbcType=NUMERIC } AS param_paper_user_id ,
		      #{ PAPER_VALID ,		jdbcType=NUMERIC } AS param_paper_valid ,
		 */
		Integer boardId = board == null ? null : board.getBoardId();
		Integer paperParentId = parentPaper == null ? null : parentPaper.getPaperId();
		Integer userId = paperUser == null ? null : paperUser.getUserId();
		
		Params params = new Params();
		params.put( "PAPER_BOARD_ID", boardId );
		params.put( "PAPER_PARENT_ID", paperParentId );
		params.put( "PAPER_ID", paperId );
		params.put( "PAPER_NAME", paperName );
		params.put( "PAPER_USER_ID", userId );
		params.put( "PAPER_VALID", paperValid );
		
		logger.trace( "getAllPapers: " + params );
		
		Paper paper = session.selectOne( "selectFromPaper", params );
		
		logger.trace( "Done getAllPapers: size: " + 1 );
		
		return paper;
		
	}
	
	/**
	 * 다음 문서 아이디를 가져온다.
	 * @param session
	 * @return
	 */
	public Integer getNextPaperId( SqlSession session ) {
		Entity entity = session.selectOne( "selectNextPaperId" );
		
		Integer number = entity.getInteger( "NEXT_ID" );
		
		return number;
	}
	
	/**
	 * 새글을 DB에 저장한다.
	 * @param session
	 * @param user
	 * @param board
	 * @param paper
	 * @return
	 */
	public int createNewPaper( SqlSession session, User user, Board board, Paper paper ) {
		/*  
		   	 NVL( #{ PAPER_ID ,	jdbcType=NUMERIC }, PAPER_ID_SEQ.NEXTVAL ),
		     #{ PAPER_BOARD_ID 	,	jdbcType=NUMERIC },
		     #{ PAPER_USER_ID 	,	jdbcType=NUMERIC },
		     #{ PAPER_NAME 		,	jdbcType=NUMERIC }, 
		     #{ PAPER_CONTENT 	,	jdbcType=NUMERIC } 
		 */ 
		
		paper.put( "PAPER_BOARD_ID" 	, 	board.getBoardId() );
		paper.put( "PAPER_USER_ID" 		, 	user.getUserId() );  
		
		int updCnt = session.insert( "insertIntoPaper", paper );
		
		if( updCnt < 1 ) {
			session.rollback();
		} else if( updCnt > 0 ) { 
			session.commit();
		}
		
		return updCnt;
	}
	
	/**
	 * 새 게시판을 생성한다.
	 * @param session
	 * @param user
	 * @param boardName
	 * @return
	 */
	public int createNewBoard( SqlSession session, User user, String boardName ) {
		/*
		NVL( #{ BOARD_ID 	,	jdbcType=NUMERIC }, BOARD_ID_SEQ.NEXTVAL ) ,
		   #{ BOARD_NAME 		,	jdbcType=VARCHAR },
		   #{ BOARD_USER_ID 	,	jdbcType=NUMERIC } 
		*/
		
		Board board = new Board(); 
		
		Integer boardId = null; 
		board.put( "BOARD_ID", boardId );
		board.put( "BOARD_USER_ID", user.getUserId() );
		board.put( "BOARD_NAME", boardName );
		
		int updCnt = session.insert( "insertIntoBoard", board );
		
		if( updCnt < 1 ) {
			session.rollback();
		} else if( updCnt > 0 ) { 
			session.commit();
		}
		
		return updCnt;
	}
	
	/**
	 * 문서를 저장한다.
	 * @param session
	 * @param paper
	 * @return
	 */
	public int savePaper( SqlSession session, Paper paper ) { 
		
		int updCnt = session.update( "updatePaper", paper );
		
		if( updCnt < 1 ) {
			session.rollback();
		} else if( updCnt > 0 ){ 
			session.commit();
		}
		
		return updCnt;
	}
	
	/**
	 * 문서를 삭제한다.
	 * @param session
	 * @param paper
	 * @return
	 */
	public int deletePaper( SqlSession session, Paper paper ) { 
		
		int updCnt = session.update( "deletePaper", paper );
		
		if( updCnt < 1 ) {
			session.rollback();
		} else if( updCnt > 0 ){ 
			session.commit();
		}
		
		return updCnt;
	} 
	
	/**
	 * 문서의 조회 기록을 남긴다.
	 * @param session
	 * @param paper
	 * @param user
	 * @return
	 */
	public int logPaperAsRead( SqlSession session , Paper paper , User user ) {
		
		Params params = new Params() ;
		params.put( "paper"	, paper );
		params.put( "user"	, user );
		
		int updCnt = session.update( "logPaperAsRead", params );
		
		if( updCnt < 1 ) {
			session.rollback();
		} else if( updCnt > 0 ){ 
			session.commit();
		}
		
		return updCnt;
	}

}
